package io.vec.demo.mediacodec;

import android.app.Activity;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Bundle;
import android.os.Environment;
import android.os.Looper;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;


import java.io.IOException;
import java.nio.ByteBuffer;

import io.vec.demo.mediacodec.LogUtils;

//https://www.jianshu.com/p/f5a1c9318524
//1.实现 SurfaceHolder.callback接口
//2.创建SurfaceView对象 addCallback 方法
//3.创建playThread线程
//4.创建MediaExtractor解封视频 设置媒体路径
//5.创建MediaCodec对象,dequenceBuffer 循环读取每一帧,通过release方法渲染到SurfaceView上
//6.关闭 MediaCodec MediaExtractor 等.
public class DecodeActivity extends Activity implements SurfaceHolder.Callback {
    private final String mVideoPath = Environment.getExternalStorageDirectory() + "/DCIM/Camera/VID_20210301_135859018.mp4";
    private PlayerThread mPlayer = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SurfaceView sv = new SurfaceView(this);
        sv.getHolder().addCallback(this);
        setContentView(sv);
        displayDecoders();
    }

    protected void onDestroy() {
        super.onDestroy();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        if (mPlayer == null) {
            mPlayer = new PlayerThread(holder.getSurface());
            mPlayer.start();
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (mPlayer != null) {
            mPlayer.interrupt();
        }
    }

    private class PlayerThread extends Thread {
        private MediaExtractor extractor;
        private MediaCodec decoder;
        private Surface surface;

        public PlayerThread(Surface surface) {
            this.surface = surface;
        }

        @Override
        public void run() {
//            MediaExtractor媒体萃取器 在播放视频前，需先解封装，MediaExtractor可以负责解封装。
            extractor = new MediaExtractor();
            try {
                extractor.setDataSource(mVideoPath);
                dumpFormat(extractor);
            } catch (IOException e) {
                Looper.prepare();
                e.printStackTrace();
                Toast.makeText(DecodeActivity.this, "视频文件不存在!", Toast.LENGTH_SHORT).show();
            }

            // 创建MediaCodec
            for (int i = 0; i < extractor.getTrackCount(); i++) {
                LogUtils.d("dddddd extractor.getTrackCount== "+extractor.getTrackCount());
                MediaFormat format = extractor.getTrackFormat(i);
                String mime = format.getString(MediaFormat.KEY_MIME);
                LogUtils.d("dddddd mime==",mime);
                // 选择视频轨道
                if (mime.startsWith("video/")) {
                    extractor.selectTrack(i);
                    try {
                        //eeeee 为特定MIME类型创建首选的 编解码器 但是，这不能用于注入特性，并且可能会创建一个不能处理特定媒体格式的编解码器。
                        decoder = createCodec(format,surface);
//                        decoder = MediaCodec.createDecoderByType(mime);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    break;
                }
            }

            if (decoder == null) {
                Log.e("DecodeActivity", "Can't find video info!");
                return;
            }
            decoder.start();

            // 给MediaCodec喂数据操作
            ByteBuffer[] inputBuffers = decoder.getInputBuffers();
            ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
            boolean isEOS = false;
            long startMs = System.currentTimeMillis();

            // 先看同步的用法。同步的主流程是在一个循环内不断调用 dequeueInputBuffer -> queueInputBuffer填充数据 -> dequeueOutputBuffer -> releaseOutputBuffer显示画面
            while (!Thread.interrupted()) {
                if (!isEOS) {
                    int inIndex = decoder.dequeueInputBuffer(10000);
                    if (inIndex >= 0) {
                        ByteBuffer buffer = inputBuffers[inIndex];
//                        通过MediaExtractor的readSampleData和advance我们可以从视频文件中不断取到所选轨道的未解码数据
                        int sampleSize = extractor.readSampleData(buffer, 0);
                        //如果返回值小于0，说明已经到的文件末尾
                        if (sampleSize < 0) {
                            Log.d("DecodeActivity", "InputBuffer BUFFER_FLAG_END_OF_STREAM");
                            decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                            isEOS = true;
                        } else {
                            decoder.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0);
                            //提前到下一个样品。如果没有更多的示例数据，则返回false 去掉后只能渲染第一帧.
                            boolean advance = extractor.advance();
                            Log.e("eeeeeeee advance==",advance+"");
                        }
                    }
                }


//                从MediaCodec取解码后的数据（实际拿到的是一个Index），一般流程是这样的
                int outIndex = decoder.dequeueOutputBuffer(info, 10000);
                Log.e("eeeeeeee outIndex==",outIndex+"");
                switch (outIndex) {
                    case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                        Log.d("DecodeActivityssss", "INFO_OUTPUT_BUFFERS_CHANGED");
                        outputBuffers = decoder.getOutputBuffers();
                        break;
                    case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                        Log.d("DecodeActivityssss", "New format " + decoder.getOutputFormat());
                        break;
                    case MediaCodec.INFO_TRY_AGAIN_LATER:
                        Log.d("DecodeActivityssss", "dequeueOutputBuffer timed out!");
                        break;
                    default:
                        ByteBuffer buffer = outputBuffers[outIndex];
                        Log.v("DecodeActivity", "We can't use this buffer but render it due to the API limit, " + buffer);

                        // We use a very simple clock to keep the video FPS, or the video
                        // playback will be too fast
                        while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {
                            try {
                                sleep(10);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                                break;
                            }
                        }
//                        通过 releaseOutputBuffer (outIndex, true)就可以让MediaCodec将这一帧输出到Surface上。
                        decoder.releaseOutputBuffer(outIndex, true);
                        break;
                }
                // 标记通知MediaCodec已经达到文件末尾
                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                    Log.d("DecodeActivityssss", "OutputBuffer BUFFER_FLAG_END_OF_STREAM");
                    break;
                }


            }

            decoder.stop();
            decoder.release();
            extractor.release();
        }
    }

    /**
     * @param extractor
     * 然后打印其轨道信息。轨道信息的遍历可以通过MediaExtractor的getTrackCount和getTrackFormat配合完成。
     * 获取音频/视频 轨道.
     */
    private void dumpFormat(MediaExtractor extractor) {
        int count = extractor.getTrackCount();
        LogUtils.e("eeeeeeeeeee playVideo: track count: " + count);
        for (int i = 0; i < count; i++) {
            MediaFormat format = extractor.getTrackFormat(i);
            LogUtils.e( "eeeeeeeeeee playVideo: track " + i + "==" + getTrackInfo(format));
        }
    }

    /**
     * @param format
     * @return
     * 打印MediaFormat的信息.
     */
    private String getTrackInfo(MediaFormat format) {
        String info = format.getString(MediaFormat.KEY_MIME);
        if (info.startsWith("audio/")) {
            info += " samplerate: "+ format.getInteger(MediaFormat.KEY_SAMPLE_RATE) + ", channel count:" + format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
        }
        else if (info.startsWith("video/")){
            info += " size:" + format.getInteger(MediaFormat.KEY_WIDTH) + "x" + format.getInteger(MediaFormat.KEY_HEIGHT);
        }
        return info;
    }

    /**
     * 枚举下设备支持的解码器.
     */
    private void displayDecoders() {
        MediaCodecList list = new MediaCodecList(MediaCodecList.ALL_CODECS);//REGULAR_CODECS参考api说明
        MediaCodecInfo[] codecs = list.getCodecInfos();
        for (MediaCodecInfo codec : codecs) {
            if (codec.isEncoder())
                continue;
            LogUtils.i("eeeeeeeeeee displayDecoders: "+codec.getName());
        }
    }

    /**
     * @param format
     * @param surface
     * @return
     * @throws IOException
     * 获取最适合的解码器.
     */
    private MediaCodec createCodec(MediaFormat format, Surface surface) throws IOException{
        MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
        MediaCodec codec = MediaCodec.createByCodecName(codecList.findDecoderForFormat(format));
        codec.configure(format, surface, null, 0);
        return codec;
    }
}